package io.hellsinger.vortex.ui.screen.storage.detail

import io.hellsinger.filesystem.attribute.PathType
import io.hellsinger.filesystem.linux.attribute.LinuxPathAttribute
import io.hellsinger.filesystem.linux.attribute.LinuxUser
import io.hellsinger.filesystem.linux.directory.LinuxDirectory
import io.hellsinger.filesystem.linux.directory.LinuxDirectoryEntry
import io.hellsinger.filesystem.linux.directory.asLinuxDirectory
import io.hellsinger.filesystem.linux.directory.treeWalker
import io.hellsinger.filesystem.linux.file.asLinuxFile
import io.hellsinger.filesystem.path.Path
import io.hellsinger.filesystem.size.SiSize
import io.hellsinger.viewmodel.BitViewModel
import io.hellsinger.viewmodel.intent
import io.hellsinger.vortex.Dispatchers
import io.hellsinger.vortex.data.model.PathItem
import io.hellsinger.vortex.data.model.name
import io.hellsinger.vortex.data.model.parseThemeType
import io.hellsinger.vortex.data.repository.AndroidStorageRepository
import io.hellsinger.vortex.data.restrictedTreeWalker
import io.hellsinger.vortex.domain.repository.StorageRepository
import io.hellsinger.vortex.foundation.isAtLeastAndroid11
import io.hellsinger.vortex.throttle
import io.hellsinger.vortex.ui.component.adapter.ItemFactory
import io.hellsinger.vortex.ui.component.adapter.SuperItem
import io.hellsinger.vortex.ui.component.adapter.TwoLine
import io.hellsinger.vortex.ui.component.adapter.title
import io.hellsinger.vortex.ui.component.adapter.twoLine
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import java.util.Locale

class StorageItemDetailViewModel(
    private val dispatchers: Dispatchers,
    private val repository: StorageRepository,
) : BitViewModel<UiState>(scopeName = "Storage Item Detail Scope") {
    private val mutableUiState = MutableUiState()

    private var jobs = mutableListOf<Job>()

    override fun getUiState(): UiState = mutableUiState

    fun load(item: PathItem) =
        intent {
            with(mutableUiState.content) {
                clear()

                title("General")
                twoLine("Index node", item.id.toString())
                twoLine("Name", item.name)
                twoLine("Path", item.path.toString())
                twoLine("Scheme", "unix")

                title("Attributes")
                twoLine("Permissions", item.permission)

                if (item.isDirectory) {
                    val index = size

                    jobs +=
                        launch {
                            val directory = item.asLinuxDirectory()

                            var size = 0L
                            var dirsCount = 0
                            var filesCount = 0

                            twoLine(
                                "Count",
                                "File: 0. Directory: 0",
                                index,
                            )
                            twoLine(
                                "Size",
                                "0B (0B)",
                                index + 1,
                            )

                            getTreeWalker(directory)
                                .filterIsInstance<LinuxDirectoryEntry<*>>()
                                .onEach { entry ->
                                    when (entry.type) {
                                        PathType.Directory -> dirsCount++
                                        PathType.File -> {
                                            filesCount++
                                            size += entry.asLinuxFile().size.original
                                        }
                                    }
                                }.catch { error ->
                                }.flowOn(dispatchers.io)
                                .throttle()
                                .collect {
                                    updateElement(
                                        index,
                                        ItemFactory.TwoLine(
                                            "Count",
                                            "File: $filesCount. Directory: $dirsCount",
                                        ),
                                    )
                                    updateElement(
                                        index + 1,
                                        ItemFactory.TwoLine(
                                            "Size",
                                            "${SiSize(size).format("#.##")} ($size bytes)",
                                        ),
                                    )
                                    update(UPDATE_ITEMS)
                                }

                            update(
                                UPDATE_ITEMS,
                            )
                        }
                }

                if (item.isFile) {
                    val size = SiSize(item.attrs.size)

                    twoLine(
                        "Size",
                        "${size.format("#.##")} (${size.original} bytes)",
                    )

                    title("Content")
                    twoLine(
                        "Mimetype",
                        "${item.mime.extension}(${item.mime.parseThemeType()})",
                    )
                }

                twoLine(
                    "Last modified time",
                    item.lastModifiedTime.toStringWithLocale(Locale.getDefault()),
                )
                twoLine(
                    "Last access time",
                    item.lastAccessTime.toStringWithLocale(Locale.getDefault()),
                )
                twoLine(
                    "Creation time",
                    item.creationTime.toStringWithLocale(Locale.getDefault()),
                )

                if (item.attrs is LinuxPathAttribute) {
                    val attrs = item.attrs as LinuxPathAttribute
                    jobs +=
                        launch {
                            val user = repository.getUser(attrs.userId)
                            title("User")

                            twoLine(
                                "Id",
                                user.id.toString(),
                            )
                            twoLine(
                                "Name",
                                user.name.decodeToString(),
                            )
                            if (user is LinuxUser) {
                                twoLine(
                                    "Home directory",
                                    user.home.decodeToString(),
                                )

                                val shell = user.shell.decodeToString()
                                if (shell.isNotEmpty()) {
                                    twoLine(
                                        "Shell",
                                        shell,
                                    )
                                }
                            }
                        }

                    jobs +=
                        launch {
                            val group = repository.getGroup(attrs.groupId)
                            title("Group")
                            twoLine(
                                "Id",
                                group.id.toString(),
                            )
                            twoLine(
                                "Name",
                                group.name.decodeToString(),
                            )
                        }
                    update(UPDATE_ITEMS)
                }
            }

            update(UPDATE_ITEMS)
        }

    private fun updateElement(
        index: Int,
        item: SuperItem,
    ) {
        mutableUiState.content[index] = item
    }

    private fun getTreeWalker(directory: LinuxDirectory<Path>) =
        if (isAtLeastAndroid11) {
            directory.restrictedTreeWalker(
                restrictedChecker = { path ->
                    path == AndroidStorageRepository.OBB_DIRECTORY_PATH || path == AndroidStorageRepository.DATA_DIRECTORY_PATH
                },
            )
        } else {
            directory.treeWalker()
        }

    companion object {
        const val UPDATE_ITEMS = 0
    }
}
